#include <simulatorV1/instruction/add.h>

bool ADD::checkFormat(std::shared_ptr<FunctionNode> functionNode) {
    if (functionNode->m_parameters.size() != 3) {
        return false;
    }
    if (functionNode->m_funcName != "ADD" && functionNode->m_funcName != "add") {
        return false;
    }

    auto para1 = functionNode->m_parameters[0];
    auto para2 = functionNode->m_parameters[1];
    auto para3 = functionNode->m_parameters[2];
    if (std::dynamic_pointer_cast<RegNode>(para1) != nullptr
        && std::dynamic_pointer_cast<RegNode>(para2) != nullptr
        && std::dynamic_pointer_cast<RegNode>(para3) != nullptr) {
        // add src1,src2,dst
        m_instFormat = InstFormat::Kind1;
        return true;
    } else if (std::dynamic_pointer_cast<ConstantNode>(para1) != nullptr
               && std::dynamic_pointer_cast<RegNode>(para2) != nullptr
               && std::dynamic_pointer_cast<RegNode>(para3) != nullptr) {
        // add scst5,src2,dst
        m_instFormat = InstFormat::Kind2;
        return true;
    }
    return false;
}

void ADD::generateIR(std::shared_ptr<FunctionNode> functionNode, llvm::Module *module,
                     std::shared_ptr<llvm::IRBuilder<>> irBuilder) {
    if (m_instFormat == InstFormat::Kind1) {
        auto para1 = functionNode->m_parameters[0];
        auto src1RegNode = std::dynamic_pointer_cast<RegNode>(para1);
        auto para2 = functionNode->m_parameters[1];
        auto src2RegNode = std::dynamic_pointer_cast<RegNode>(para2);
        auto para3 = functionNode->m_parameters[2];
        auto dstRegNode = std::dynamic_pointer_cast<RegNode>(para3);


        auto dstRegPtr = module->getNamedGlobal(TagName[static_cast<u32>(dstRegNode->m_regKind)]);
        auto src1RegPtr = module->getNamedGlobal(TagName[static_cast<u32>(src1RegNode->m_regKind)]);
        auto src2RegPtr = module->getNamedGlobal(TagName[static_cast<u32>(src2RegNode->m_regKind)]);

        auto result = irBuilder->CreateAdd(irBuilder->CreateLoad(src1RegPtr), irBuilder->CreateLoad(src2RegPtr));
        irBuilder->CreateStore(result, dstRegPtr);
    } else if (m_instFormat == InstFormat::Kind2) {
        auto para1 = functionNode->m_parameters[0];
        auto cstNode = std::dynamic_pointer_cast<ConstantNode>(para1);
        auto para2 = functionNode->m_parameters[1];
        auto src2RegNode = std::dynamic_pointer_cast<RegNode>(para2);
        auto para3 = functionNode->m_parameters[2];
        auto dstRegNode = std::dynamic_pointer_cast<RegNode>(para3);

        auto cst = static_cast<u32>(cstNode->m_value);
        auto scst5 = extractBitsAndToInt<5>(cst);

        auto dstRegPtr = module->getNamedGlobal(TagName[static_cast<u32>(dstRegNode->m_regKind)]);
        auto src2RegPtr = module->getNamedGlobal(TagName[static_cast<u32>(src2RegNode->m_regKind)]);

        auto result = irBuilder->CreateAdd(irBuilder->getInt32(scst5), src2RegPtr);
        irBuilder->CreateStore(result, dstRegPtr);
    }
}

std::vector<std::string> ADD::formatToString() {
    std::vector<std::string> formatStr;
    formatStr.emplace_back("ADD src1,src2,dst");
    formatStr.emplace_back("ADD scst5,src2,dst");
    return std::move(formatStr);
}